#pragma once
#include <iostream>
#include <vector>
#include <cstring>
#include <pthread.h>
#include <cassert>
#include <semaphore.h>

const int cap = 5;

template<class T>
class ring_queue
{
private:
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem);
        assert(n == 0);
        (void)n;
    }
    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        assert(n == 0);
        (void)n;
    }
public:
    ring_queue(const int& max_cap = cap)
    :_queue(max_cap)
    ,_cap(max_cap)
    {
        int n = sem_init(&_sapce_sem, 0, _cap);
        assert(n == 0);
        n = sem_init(&_data_sem, 0, 0);
        assert(n == 0);
        (void)n;

        pthread_mutex_init(&_cmutex, nullptr);
        pthread_mutex_init(&_pmutex, nullptr);

        _productor_step = _consumer_step = 0;
    }

    void push(const T& in)
    {
        P(_sapce_sem);
        pthread_mutex_lock(&_pmutex);
        _queue[_productor_step++] = in;
        _productor_step %= _cap;
        pthread_mutex_unlock(&_pmutex);
        V(_data_sem);
    }
    void pop(T* out)
    {
        P(_data_sem);
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_consumer_step++];
        _consumer_step %= _cap;
        pthread_mutex_unlock(&_cmutex);
        V(_sapce_sem);
    }

    ~ring_queue()
    {
        sem_destroy(&_sapce_sem);
        sem_destroy(&_data_sem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private:
    std::vector<T> _queue;
    int _cap;
    sem_t _sapce_sem;
    sem_t _data_sem;
    int _productor_step;
    int _consumer_step;
    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
};